/*
@Copyright:Copyright(c)2012
@Company:Morningstar
@Title:Fairvalue Chart
@description:construct a fairvalue chart
@author:Alex
@Date:2011.10.9
@version:1.0
@History:
*/

/*
@Dependence
//js
require(lib/jquery.js)
require(lib/jquery.json.min.js)
require(lib/mstar.flot.js)
require(lib/excanvas.min.js)
require(js/formatter/qs_formatter_en_us.js)
require(js/com/chartDraw.js)
require(js/com/chartUtil.js)
require(js/com/fairvalue_datamanager.js)
require(js/ui/treeview.js)

//css
require(qs.css)
require(style.css)
require(fairvalue.css)
*/

/*
@Name:QS_MktFairValueChart
@Description:create the chart of fairvalue
@Param:
	container - the container that contain the fairvalue chart
    cfg 	  - the optional configuration
@Return: undefined
*/
function QS_MktFairValueChart(container, cfg){
	var self=this;
	var themeKey=QS_Theme||'default';
	var themeCfg={
		'default':'#000',
		'black':'#ccc',
		'gray':'#ccc'
	};
	var midLineColor=themeCfg[themeKey]||'#000';
	this.cfg={//default config
		width:0,
		height:0,
		XLabelHeight:16,
		YLabelWidth:30,
		lineWidth:2,
		aboveColor:"#ca3239",//"#a5001f",//65A125
		belowColor:"#65a125",//"#00487b",//CA3239
		midLineColor:midLineColor,
		aboveFillColor:"#e09497",//"#e6bdc5",
		belowFillColor:"#b0ce90",//"#baccd9",
		LabelColor:"#646464",
		gridLineColor:"#ccc",
		bottomLineColor:"#999",
		XLabelTextAlign:"center",
		YLabelAlign:"left",
		bHasTips:true,
		bHasCloseLine:false
	};
	$.extend(true,this.cfg,cfg);

	//create dom
	this.$container=(typeof container)=="string" ? $("#"+container) : container;
	
	this.$chartTips=$('<div class="fv-chart-tips">'+
						'<span>Price/Fair Value</span>&nbsp<span class="fv-date"></span>'+
						'<div class="fv-val"></div>'+
				      '</div>').appendTo(this.$container);
	
	this.$chartTips_date=this.$chartTips.find(".fv-date");
	this.$chartTips_val=this.$chartTips.find(".fv-val");
		
	this.$chartCtn=$('<div class="fv-chart-ctn"></div>').appendTo(this.$container);
	
	//set the width and height for flot
	this.$chartCtn.css({width:this.cfg.width,height:this.cfg.height});
	
	//create a new flot
	this.flotObj = new mstar.flot(this.$chartCtn,this._getOption());
	
	this._onResize();
}


QS_MktFairValueChart.prototype={

	/*
	@Name:init
	@Description:call this function to initialize the chart
	@Param:
		chartData - data point array to construct a fairvalue chart
		dateIndex - date array that contain the second count from 1900.1.1
	@Return: undefined
	*/
	init:function(chartData,dateIndex){
		if(!chartData.length ||!dateIndex){
			this.dataAvailable = false;
			this.flotObj.init([{data:chartData}]);	
			return;
		}
		
		this.dataAvailable = true;
		
		//save dateIndex object
		this.dateIndex=dateIndex;
		
		this._bindEvent();
		
		//generate the corresponding X label name. (day/month/year)
		var labelMode=this._getLabelMode(dateIndex[0],dateIndex[dateIndex.length-1]);
		var ticks=this._createTicks(labelMode);
		
		this.flotObj.setOption({xaxis:{tickSize:1, ticks:ticks}});
		this.flotObj.init([{data:chartData}]);
	},

	drawText:function(txt){
		var ctx = this.flotObj.ctx;
		ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
		ctx.font = "20px";
		ctx.fillStyle = 'black';
		ctx.textAlign = 'center';
		ctx.fillText(txt, ctx.canvas.width/2, ctx.canvas.height/2, 300);
	},
	
	_onChartHover:function(item){
		if(!item)return;
		if(!this.dataAvailable)return;
		//show the tips on hover
		
		//cache the height
		if(!this.chartTipsH){
			this.chartTipsH = this.$chartTips.height();
		}
		
		var subRect=new this._Rect(item.pageX,item.pageY,this.$chartTips.width(), this.chartTipsH);

		//get adapt position for tips
		var adaptRect=this._calcAdaptPosition(this.ctnRect,subRect);
		this.$chartTips.offset({left:adaptRect.left,top:adaptRect.top});

		//set some text
		var date=RTQ_ChartUtil.formatDateToVBDate(RTQ_ChartUtil.toDateFromIndex(this.dateIndex[item.dataIndex]));
		this.$chartTips_date.text(date);
		this.$chartTips_val.text(item.datapoint[1].toFixed(2));
	},

	//fixme: if when the chart preparing showing, the mouse has been in the chart area, there will no mouseover message.
	_onMouseOver:function(){
		if(!this.dataAvailable)return;
		if(this.$chartTips.css("display")=="none"){
			this.$chartTips.css("display","block");
		}
	},

	_onMouseOut:function(e){
		//hide tips
		var self=this;
		if(!this._contains(this.$container[0],e.relatedTarget)){
			this.$chartTips.css("display","none");
			setTimeout(function(){self.flotObj.clearHighlights()},100);
		}
	},
	
	_bindEvent:function(){
		if(this.cfg.bHasTips){
			var self=this;
			this.$chartCtn.unbind("plothover").bind("plothover", function(event, pos, item){
				self._onChartHover(item);
			});

			this.$chartCtn.unbind("mouseout").bind("mouseout", function(e) { 
			   self._onMouseOut(e);
			} );

			this.$chartCtn.unbind("mouseover").bind("mouseover", function() {
				self._onMouseOver();
			} );
		}
	},
	
	_formatYlabel:function(v,axis){
		//replace 1.00 of Y label with "FV"
		return parseFloat(v) == 1 ? "FV" : (typeof(QS_NumFmt)!="undefined"?QS_NumFmt.Fmt2(v,true," ",axis.tickDecimals):v.toFixed(axis.tickDecimals)) ;
	},
	
	/*
	@Name:changeOutSize
	@Description:give a width and height to change the size of fairvalue chart.
	@Param:
		w - the width
		h - the height
	@Return: undefined
	*/
	changeOutSize:function(w,h){
		this.flotObj.changeOutSize(w,h);
		this.cfg.width=w;
		this.cfg.height=h;
		this._onResize();
	},
	
	_onResize:function(){
		var ctnOffset=this.$chartCtn.offset();
		this.ctnRect=new this._Rect(ctnOffset.left,ctnOffset.top,this.cfg.width,this.cfg.height);
	},
	
	//calc a label mode
	_getLabelMode:function(startDateIndex,endDateIndex){
		var diffDays=endDateIndex-startDateIndex;
		var labelMode;
		if(diffDays > 366){
			//use year
			labelMode="y";
		}else if(diffDays<=366&&diffDays>31){
			//use month
			labelMode="m";
		}else{
			//use day
			labelMode="d";
		}
		return labelMode;
	},
	
	//base on the label mode, create the ticks for chart data.
	_createTicks:function(labelMode){
		var ticks=[];
		var label;
		for(var i=0;i<this.dateIndex.length;i++){
			if(labelMode=="y"){
				var currentYear=RTQ_ChartUtil.toDateFromIndex(this.dateIndex[i]).getFullYear();
				if(currentYear != label){
					label = currentYear;
					ticks.push([i,label]);
				}
			}else if(labelMode=="m"){
				var currentMonth=RTQ_ChartUtil.toDateFromIndex(this.dateIndex[i]).getMonth();
				if(currentMonth != label){
					label = currentMonth;
					ticks.push([i,QS_DateFmt.Mo[label]]);
				}
			}else if(labelMode=="d"){
				var currentDay=RTQ_ChartUtil.toDateFromIndex(this.dateIndex[i]).getDate();
				if(currentDay != label){
					label = currentDay;
					ticks.push([i,label]);
				}
			}else{
				label=RTQ_ChartUtil.formatDate(RTQ_ChartUtil.toDateFromIndex(this.dateIndex[v]));
				ticks.push([i,label]);
			}
		}//end for
		
		return ticks;
	},
	
	_contains:function(a, b) {
		return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(b) & 16);
	},
	
	_Rect:function(left,top,width,height){
		this.left=left;
		this.top=top;
		this.width=width;
		this.height=height;
	},

	//make the tips position always in the chart area.
	_calcAdaptPosition:function(ctnRect,subRect){
		var result = new this._Rect(subRect.left,subRect.top,subRect.width,subRect.height);
		if(subRect.left+subRect.width>ctnRect.left+ctnRect.width){
			//X overflowed
			result.left-=result.width;
		}
		if(subRect.top+subRect.height>ctnRect.top+ctnRect.height){
			//Y overflowed
			result.top-=result.height;
		}

		return result;
	},
	
	_getOption:function(){
		var self=this;
		return {
			xaxis:{ticks:null,/*tickFormatter:function(v,axis){return self._formatXlabel(v,axis)}*/labelHeight:this.cfg.XLabelHeight,textAlign:this.cfg.XLabelTextAlign},
			yaxis:{align:this.cfg.YLabelAlign,labelWidth:this.cfg.YLabelWidth,labelHeight:12,minTopLine:1,minBottomLine:1,tickFormatter:function(v,axis){return self._formatYlabel(v,axis)}},
			lines:{
				lineWidth: this.cfg.lineWidth, // in pixels
				lineType:"ab",//"line,mountain,dot,ab",
				ab:{aColor:this.cfg.aboveColor,bColor:this.cfg.belowColor,mColor:this.cfg.midLineColor,midLineValue:1,aFillColor:this.cfg.aboveFillColor,bFillColor:this.cfg.belowFillColor,bHasCloseLine:this.cfg.bHasCloseLine}
			},
			grid:{hoverable:true,clickable:true,color:this.cfg.LabelColor,tickColor:this.cfg.gridLineColor,bottomColor:this.cfg.bottomLineColor,_borderColor:null}
		};
	}
}


/*
@Name:QS_MktFairValue
@Description:create a fairvalue chart
@Param:
	container - the container that contain the fairvalue chart
    cfg 	  - the configuration(optional)
@Return: undefined
*/
function QS_MktFairValue(container, cfg){

	this.$container=(typeof container)=="string" ? $("#"+container) : container;

	this.cfg={
		width:this.$container.width()||600,
		height:this.$container.height()||300,
		sidebarWidth:175,
		defaultTicker:{val:'AllStocks', txt:'All Rated Stocks'},
		defaultPeriod:'M1',
		hasSidebar:false
	}
	$.extend(true, this.cfg, cfg);
	
	this.cfg.fvWidth = this.cfg.hasSidebar ? (this.cfg.width - this.cfg.sidebarWidth - 2) : this.cfg.width;//margin-left:2px
	this.$container.css({width:this.cfg.width, height:this.cfg.height});
	this.currentTicker = this.cfg.defaultTicker;
	this.currentPeriod = this.cfg.defaultPeriod;
	if(this.cfg.currentPeriod){
		this.currentPeriod=this.cfg.currentPeriod;
	}
	if(this.cfg.currentTicker){
		this.currentTicker=this.cfg.currentTicker;
	}
	this.gFairValue_DataManager=new QS_FairValue_DataManager();
	this._init();
}

QS_MktFairValue.prototype={
	_init:function(){
		var self=this;
		
		//init dom
		this.$container.append('<div class="fairvalue">'+
									'<div class="fv-sidebar"></div>'+
									'<div class="fv-fairvalue">'+
										'<div class="fv-tabmenuwrap">'+
											'<div class="fv-tabmenu"></div>'+
										'</div>'+
										'<div class="fv-chartwrap">'+
											'<div class="fv-chartinfo"></div>'+
											'<div class="fv-chart"></div>'+
										'</div>'+
									'</div>'+
								'</div>');
								
		this.$container.find('.fairvalue').addClass('clearfix');
		
		if(this.cfg.hasSidebar){
			
			this.$container.find('.fv-fairvalue').css({'margin-left':this.cfg.sidebarWidth+2, 'position':'fixed', 'width':(this.cfg.width-this.cfg.sidebarWidth-2)});
			this.$sidebar = this.$container.find('.fv-sidebar');
			var onclick = function(node){self.onTickerChange(node)};
			var setting = new QS_FV_SettingMenu(this.$sidebar, {sidebarWidth:this.cfg.sidebarWidth}, {onclick:onclick});
			setting.selectNodeByVal(this.cfg.defaultTicker.val);
		}
		
		this.$tabMenu=this.$container.find(".fv-tabmenu");
		//init menu
		var tabData = [{txt:"1 Mo",val:"M1",closable: false},
						{txt:"3 Mo",val:"M3",closable: false},
						{txt:"YTD",val:"YTD",closable: false},
						{txt:"1 Yr",val:"Y1",closable: false},
						{txt:"3 Yr",val:"Y3",closable: false},
						{txt:"5 Yr",val:"Y5",closable: false},
						{txt:"MAX",val:"AL",closable: false}];
		var tabMenu = new Util.TabMenu(this.$tabMenu[0], tabData, {
			onActive: function(tab){
				self.currentPeriod = tab.val;
				if(self.currentTicker.val){					
					self._switchChart(self.currentTicker.val, self.currentPeriod);
				}
			}
		}, {
			activeTabId:this.currentPeriod
		});
		
		var $chartInfo=this.$container.find(".fv-chartinfo").append('<div class="fv-stock-info">'+
																		'<span>Coverage Universe:&nbsp</span><span class="fv-data-type"></span>'+
																		'<span class="fv-data-separate">|</span>'+
																		'<span>Today\'s Ratio:&nbsp</span><span class="fv-data-ratio"></span>'+
																	'</div>'+
																	'<div class="fv-date-info">'+
																		'<span class="fv-startdate"></span> - <span class="fv-enddate"></span>'+
																		'<span class="fv-fairvalue-img">Fair Value</span>'+
																		'<span class="fv-overvalued-img">Overvalued</span>'+
																		'<span class="fv-undervalued-img">Undervalued</span>'+
																	'</div>');

		this.$dataType=this.$container.find(".fv-data-type");
		this.$dataRatio=this.$container.find(".fv-data-ratio");
		this.$startDate=this.$container.find(".fv-startdate");
		this.$endDate=this.$container.find(".fv-enddate");
		this.$chartCtn=this.$container.find(".fv-chart");

		//init chart
		this.textHeight=this.$tabMenu.height()+$chartInfo.height();
		this.typeTextW = this.cfg.fvWidth;
		this.$container.find(".fv-stock-info").children().not(this.$dataType).each(function(){
			self.typeTextW -= $(this).outerWidth(true);
		})
		
		var cfg=$.extend(true,{},this.cfg);
		cfg.width = cfg.fvWidth;
		cfg.height -= this.textHeight;
		this.chart = new QS_MktFairValueChart(this.$chartCtn,cfg);
	},

	onTickerChange:function(node){
		this.currentTicker = node.data;
		if(this.currentPeriod){
			this._switchChart(this.currentTicker.val, this.currentPeriod);
		}
	},
	_switchChart:function(ticker, period){
		var self = this;
		self.gFairValue_DataManager.loadData(ticker, period, function(data){
			if(!data)return;
			self._updateData($.evalJSON(data));
		});
	},
	
	_updateData:function(jData){
		var defaultTxt = '';
		if(!jData || jData.errorCode!=0 || jData.DateIndex.length==0 || jData.FairValue.length==0)defaultTxt = ' ';
		
		//set today's ratio
		this.$dataRatio.text(defaultTxt || jData.FairValue[jData.FairValue.length-1]);
		
		//set data type
		this.$dataType.text(defaultTxt || this.currentTicker.txt).attr('title', defaultTxt || this.currentTicker.txt);
		this.$dataType.css({'max-width':this.typeTextW - this.$dataRatio.outerWidth(true)});
		
		//set start date and end date
		this.$startDate.text(defaultTxt || RTQ_ChartUtil.formatDateToVBDate(RTQ_ChartUtil.toDateFromIndex(jData.DateIndex[0])));
		this.$endDate.text(defaultTxt || RTQ_ChartUtil.formatDateToVBDate(RTQ_ChartUtil.toDateFromIndex(jData.DateIndex[jData.DateIndex.length-1])));
		
		//update chart
		if(defaultTxt == ' '){
			return this.chart.init([],[]);
		}
		var chartData = this._convertToChartData(jData.FairValue);
		this.chart.init(chartData, jData.DateIndex);
	},
	
	_convertToChartData:function(data){
		var chartData=[];
		for(var i=0;i<data.length;i++){
			chartData.push([i,parseFloat(data[i])]);
		}
		return chartData;
	},

	/*
	@Name:changeOutSize
	@Description:give a width and height to change the size of fairvalue chart.
	@Param:
		w - the width
		h - the height
	@Return: undefined
	*/
	changeOutSize:function(w,h){
		this.$container.css({width:w,height:h});
		this.chart.changeOutSize(w-(this.cfg.hasSidebar?this.cfg.sidebarWidth:0), h-this.textHeight);
	},
	saveConfig:function(){        
        return {
            currentTicker:this.currentTicker,	//current ticker
            currentPeriod:this.currentPeriod	//current period
        }
    },
	clear:function(){
		for(var p in this){
			if(!p || p == 'clear'){continue;}
			this[p] = null;
			delete this[p];
		}
    }
}